#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>

#include "Task.hpp"
#include "ThreadPool.hpp"

namespace Server
{
    using namespace std;
    
    //需要提前声明，因为下面的ThreadData用到了tcpServer
    class tcpServer;

    class ThreadData
    {
    public:
        ThreadData(tcpServer* self, int sock) 
        : _self(self), _sock(sock)
        {}
    public:
        tcpServer* _self;
        int _sock;
    };

    class tcpServer
    {
    private:
        int _listensock;
        uint16_t _port;
    public:
        tcpServer(const uint16_t& port)
        : _port(port), _listensock(-1)
        {}

        void InitServer()
        {
            // 1.创建套接字
            _listensock = socket(AF_INET, SOCK_STREAM, 0);
            if(_listensock < 0)
            {
                exit(2);
            }
            cout << "create socket success" << endl;

            // 2.绑定
            struct sockaddr_in local;
            memset(&local, 0, sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = htons(_port);
            local.sin_addr.s_addr = INADDR_ANY;

            int n_bind = bind(_listensock, (struct sockaddr*)&local, sizeof(local));
            if(n_bind < 0)
            {
                exit(3);
            }
            cout << "bind socket success" << endl;
            // 3.监听，设置套接字socket状态为监听状态
            int n_listen = listen(_listensock, 5);
            if(n_listen < 0)
            {
                exit(4);
            }
            cout << "listen socket success" << endl;
        }

        void Start()
        {
            // 初始化线程池并启动线程池
            ThreadPool<Task>::getInstance()->run();
            cout << "Thread init success" << endl;
            // 4.accept
            while(1)
            {
                struct sockaddr_in peer;
                socklen_t len = sizeof(peer);
                // accept成功返回一个文件描述符，用来和Client通信，而这里的_sock是用来监听链接到来，获取新链接的。
                int sock = accept(_listensock, (struct sockaddr*)&peer, &len);
                if(sock < 0)
                {
                    continue;
                }
                cout << "accept a new link success, get new sock: " <<  sock << endl;

                // 5.这里就是一个sock，未来通信我们就用这个sock，面向字节流的，后续全部都是文件操作！
                // version 1
                //serviceIO(sock);
                //close(sock); //对一个已经使用完毕的sock，我们要关闭这个sock，要不然会导致，文件描述符泄漏

                // version 2 多进程
                // pid_t id = fork();
                // if(id == 0) // 子进程
                // {
                //     // 子进程会有自己独立的进程地址空间
                //     close(_listensock);
                //     if(fork() > 0) exit(0);
                //     serviceIO(sock);
                //     close(sock);
                //     exit(0);
                // }
                // close(sock);
                // // 父进程
                // // 子进程结束需要父进程来回收，避免僵尸进程
                // pid_t ret = waitpid(id, nullptr, 0);
                // if(ret>0)
                // {
                //     std::cout << "waitsuccess: " << ret << std::endl;
                // }

                // version 3 多线程
                // pthread_t tid;
                // ThreadData* td = new ThreadData(this, sock);
                // pthread_create(&tid, nullptr, threadRoutine, td);

                // version 4 线程池
                ThreadPool<Task>::getInstance()->push(Task(sock, serviceIO));
            }
           
        }
        
        // static void* threadRoutine(void* args)
        // {
        //     // 线程分离，不用主线程等待回收
        //     pthread_detach(pthread_self());
        //     ThreadData* td = static_cast<ThreadData*>(args);
        //     td->_self->serviceIO(td->_sock);
        //     // 记得关闭文件描述符和delete，避免资源泄漏
        //     close(td->_sock);
        //     delete td;
        //     return nullptr;
        // }

        

        ~tcpServer()
        {}
    };
    
}